#include "include/shyper_service.h"
#include "include/blk.h"
#include "include/hvc.h"
#include "include/irq.h"

struct class *shyper_class;
struct shyper_dev shyper_dev;
struct timer_list kernel_timer;
struct timer_list guset_os_timer[VM_NUM];

dev_t devno;
int irqno;
int irqno_blk;
u64 cur_vm_id;

void get_irqno_from_node_and_register(void) {
    // register vm_service
    struct device_node *np = of_find_node_by_path("/vm_service");
    int ret;

    if (!np) {
        WARNING("%s: find node failed", __func__);
    }

    // 通过节点去获取到中断号码
    irqno = irq_of_parse_and_map(np, 0);
    ret = request_irq(irqno, irq_handler, IRQF_TRIGGER_RISING, "shyper",
                      NULL); // TODO
    if (ret == 0) {
        INFO("register irq success, irq_id is %d\n", irqno);
    } else {
        WARNING("%s: registre irq fail, errno is %d, irq_id is %d\n", __func__,
                ret, irqno);
    }

    handle_hvc(HVC_IVC, HVC_IVC_UPDATE_MQ, 0);
}

int cmd2hvcfid(uint32_t cmd) { return (cmd & 0xff00) >> 8; }

int cmd2event(uint32_t cmd) { return (cmd & 0x00ff); }

/*文件打开函数*/
int shyper_open(struct inode *inode, struct file *filp) {
    // INFO("open shyper dev");
    return 0;
}

/*文件释放函数*/
int shyper_release(struct inode *inode, struct file *filp) {
    //INFO("release shyper dev");
    return 0;
}

static long shyper_ioctl(struct file *file, unsigned int cmd,
                         unsigned long arg) {
    u64 hvc_fid = cmd2hvcfid(cmd);
    u64 event = cmd2event(cmd);
    // INFO("shyper_ioctl: cmd is %x\n", cmd);
    if (!handle_hvc(hvc_fid, event, arg)) {
        WARNING("[hvc_fid: 0x%08x, event: 0x%08x]: handle hvc failed",
                (u32)hvc_fid, (u32)event);
        return -1;
    }

    return 0;
}

static ssize_t shyper_read(struct file *filp, char __user *buffer, size_t count,
                           loff_t *ppos) {
    void *addr = queue_pop(shyper_dev.usr_arg_queue);
    if (addr == 0) {
        return 0;
    }
    if (copy_to_user(buffer, addr, count))
        return -EFAULT;
    return count;
}
/*文件操作结构体*/
static const struct file_operations shyper_fops = {
    .open = shyper_open,
    .release = shyper_release,
    .compat_ioctl = shyper_ioctl,
    .unlocked_ioctl = shyper_ioctl,
    .read = shyper_read,
};

/*设备驱动模块加载函数*/
static int shyper_service_init(void) {
    int i, ret = 0;

    INFO("Init shyper service\n");
    /*初始化cdev结构*/
    cdev_init(&shyper_dev.cdev, &shyper_fops);
    /* 注册字符设备 */

    ret = alloc_chrdev_region(&devno, 0, 2, "shyper");

    if (ret < 0) {
        WARNING("%s: alloc dev fail, errno %d", __func__, ret);
    } else {
        INFO("major %d minor %d", MAJOR(devno), MINOR(devno));
    }
    cdev_add(&shyper_dev.cdev, devno, 2);
    shyper_class = class_create(THIS_MODULE, "shyper");
    device_create(shyper_class, NULL, devno, NULL, "shyper");

    shyper_dev.cfg_queue = vmalloc(VM_PAGE_SIZE);
    shyper_dev.send_queue = vmalloc(VM_PAGE_SIZE);
    for (i = 0; i < VM_NUM_MAX; i++) {
        shyper_dev.receive_queue[i] = (u64)vmalloc(VM_PAGE_SIZE);
    }
    // for (i = 0; i < VM_NUM_MAX; i++) {
    //     INFO("queue init, receive_queue[%d] is %016llx\n", i,
    //          (u64)shyper_dev.receive_queue[i]);
    // }
    shyper_dev.cfg_ptr = shyper_dev.cfg_queue - VM_PAGE_SIZE / VM_NUM_MAX;

    shyper_dev.usr_arg_queue = queue_alloc(256, 1024);

    for (i = 0; i < 4; i++) {
        shyper_dev.receive_idx[i] = -1;
        shyper_dev.send_idx[i] = -1;
    }

    ret = hvc_call(kva2ipa((u64)&cur_vm_id), 0, 0, 0, 0, 0, 0,
                   HVC_MODE(HVC_VMM, HVC_VMM_GET_VM_ID));
    if (ret != 0) {
        WARNING("get vm id failed\n");
        return 0;
    }

    INFO("VM [%lld] is installing kernel module\n", cur_vm_id);

    get_irqno_from_node_and_register();
    register_misc();

    return 0;
}

/*模块卸载函数*/
static void __exit shyper_service_exit(void) {
    int i;
    INFO("exit shyper service\n");
    del_timer(&kernel_timer);
    for (i = 0; i < VM_NUM; ++i) {
        del_timer(&guset_os_timer[i]);
    }
    free_irq(irqno, NULL);
    queue_free(shyper_dev.usr_arg_queue);
    vfree(shyper_dev.cfg_queue);
    for (i = 0; i < VM_NUM_MAX; ++i) {
        vfree((void *)shyper_dev.receive_queue[i]);
    }
    vfree(shyper_dev.send_queue);
    cdev_del(&shyper_dev.cdev);         /*注销设备*/
    class_destroy(shyper_class);        /*注销class*/
    unregister_chrdev_region(devno, 2); /*释放设备号*/
    deregister_misc();
}

MODULE_LICENSE("GPL v2");

module_init(shyper_service_init);
module_exit(shyper_service_exit);